#include "CenarioJogo.h"

void *font = GLUT_BITMAP_8_BY_13;
void *font2 = GLUT_BITMAP_HELVETICA_18;

CenarioJogo::CenarioJogo(vector<Jogador *> jogadores)
{
	players = jogadores;
	jogadas.clear();
	turn = 0;
	pecaanim.clear();
	anim = false;
	tumbling = false;
	tumblingsize = 0;
	endgame = false;
	winner = -1;
	timer = 100;
	gamemsg.clear();
}

void CenarioJogo::init()
{
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
	glutSetWindowTitle("Tumbling Down");
	glEnable(GL_LIGHTING);
	glClearColor(0,0,0.1,1);
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
	float ambient[4] = {1, 1, 1, 1};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
	float light0_pos[4] = {0, 0, 0, 1.0};
	light0 = new CGFlight(GL_LIGHT0, light0_pos);
	light = true;
	glPushMatrix();
		t = new Tabuleiro();
	glPopMatrix();
	glPushMatrix();
	cand = new Candeeiro();
	glPopMatrix();
	glPushMatrix();
	start = clock();
	glPopMatrix();
	com = new Socket();
	com->Connect();
}

void CenarioJogo::display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	CGFscene::activeCamera->applyView();
	glPushMatrix();
	glRotatef(180,0,1,0);
	glPushMatrix();
	glPushMatrix();
		glTranslatef(0,10,0);
		if(light)
		{
			light0->enable();
			light0->draw();
		}
		else
		{
			light0->disable();
		}
		glPushMatrix();
			glScalef(0.5,0.5,0.5);
			glTranslatef(0,0.25,0);
			float CandAmbient[4] = {0,0,0,1};
			float CandDifuse[4] = {1,1,1,1.0};
			float CandSpec[4] = {1,1,1,1.0};
			float CandShineness = 100.0;
			CGFappearance* null = new CGFappearance(CandAmbient,CandDifuse,CandSpec,CandShineness);
			null->apply();
			cand->draw();
		glPopMatrix();
	glPopMatrix();
	glPushMatrix();
		showPlayers();
	glPopMatrix();
	glPushMatrix();
		t->draw();
	glPopMatrix();
	if(!players[turn]->getHumano() && !endgame)
	{
		if(!anim)
		{
			getCPUPlay();
		}
	}
	if(anim)
	{
		glPushMatrix();
			x = x + dx;
			y = y + dy;
			glTranslatef(x-3.5,0.21,y-3.5);
			for(unsigned int i = 0; i < pecaanim.size(); i++)
			{
				glPushMatrix();
				glTranslatef(0,0.25*i,0);
				pecaanim[i].draw();
				glPopMatrix();
			}
		glPopMatrix();
		if(tumbling)
		{
			int cont = (100 - timer);
			int part = (100 / tumblingsize);
			float percd = cont / 100.0;
			if(((cont % part) == 0) && (cont > 0))
			{
				if((x < 0) || (y < 0) || (x > 7) || (y > 7))
				{
					timer = 1;
					if((x < 0) || (x > 7))
						yf = round(y);
					if((y < 0) || (y > 7))
						xf = round(x);
				}
				else
				{
					t->increaseCasa(round(x),round(y),pecaanim[0].getCor());
					pecaanim.erase(pecaanim.begin());
				}
			}
		}
		timer--;
		if(timer <= 0)
		{
			anim = false;
			if(xf < 0)
				xf = 0;
			if(yf < 0)
				yf = 0;
			if(xf > 7)
				xf = 7;
			if(yf > 7)
				yf = 7;
			t->setCasa(xf,yf,pecaanim);
			actualx = xf;
			actualy = yf;
			jogadas.back()->setFinalPosition(round(xf),round(yf));
			timer = 100;
			if(!(t->hasKings(turn)) && !endgame)
			{
				changeTurn();
				endgame = true;
			}
			if(isGameOver())
				endTheGame();
			else
				changeTurn();
		}
	}
	glPopMatrix();
	glPopMatrix();
	glPushMatrix();
		showInfo();
	glPopMatrix();
	glutSwapBuffers();
	glFlush();
}

void CenarioJogo::getCPUPlay()
{
	char* jl;
	if(turn == 0)
		jl = "b";
	else
		jl = "r";
	string cpuplay = com->analisaJogadaCPU((char*) convertGameStatus().c_str(),jl);
	unsigned int Xi = cpuplay[1]- '0';
	unsigned int Yi = cpuplay[3]- '0';
	unsigned int Xf = cpuplay[5] - '0';
	unsigned int Yf = cpuplay[7]- '0';
	char tm = cpuplay[10];
	bool tipom;
	if(tm == 'T')
		tipom = true;
	else
		tipom = false;
	processMovement(Xi,Yi,Xf,Yf,tipom);
}

void CenarioJogo::changeTurn()
{
	if(turn == 0)
		turn = 1;
	else
		turn = 0;
}

int CenarioJogo::round(float num)
{
	if(((int)(num * 10) % 10) < 5)
		return floor(num);
	else
		return ceil(num);
}

void CenarioJogo::showPlayers()
{
	glEnable(GL_FLAT);
	float TextAmbient[4] = {1.0,1.0,1.0,0};
	float TextAmbient2[4] = {0,0,0,1};
	CGFappearance* null = new CGFappearance(TextAmbient,TextAmbient,TextAmbient,0);
	null->apply();
    glPushMatrix();
		glColor3f(1.0,1.0,1.0);
		glPushMatrix();
			glRasterPos3f(-4,3,-4);
			std::string nometexto = players[0]->getNome();
			std::string::iterator istr = nometexto.begin();
			for(unsigned int istr = 0; istr < nometexto.length(); istr++)
			{
				glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, nometexto[istr]);
			}
		glPopMatrix();
		glPushMatrix();
			glRasterPos3f(4,3,4);
			std::string nometexto2 = players[1]->getNome();
			std::string::iterator istr2 = nometexto2.begin();
			for(unsigned int istr2 = 0; istr2 < nometexto2.length(); istr2++)
			{
				glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, nometexto2[istr2]);
			}
		glPopMatrix();
	glPopMatrix();
	glDisable(GL_FLAT);
}

void CenarioJogo::showInfo()
{
    glPushMatrix();                    
    glLoadIdentity();    
    glMatrixMode(GL_PROJECTION);    
    glPushMatrix();                 
    glLoadIdentity();                
    gluOrtho2D(0, 400, 0, 300);
	float color[4] = {1, 1, 1, 1};
    stringstream ss;
    ss << std::fixed << std::setprecision(3);
	clock_t now = clock();
	clock_t tempo = now - start;
	if(endgame)
		tempo = endtime - start;
	int sec = (((int)tempo)/CLOCKS_PER_SEC) % 60;
	int min = ((((int)tempo)/CLOCKS_PER_SEC) / 60) % 3600;
	int hor = (((int)tempo)/CLOCKS_PER_SEC) / 3600;
    ss << "Tempo: ";
	if(hor > 0)
	{
		if(hor < 9)
			ss << 0 << hor << "h ";
		else
			ss << hor << "h ";
	}
	if(min > 0)
	{
		if(min < 9)
			ss << 0 << min << "m ";
		else
			ss << min << "m ";
	}
	if(sec < 9)
		ss << 0 << sec << "s" << ends;
	else
		ss << sec << "s" << ends;
    drawString(ss.str().c_str(), 1, 290, color, font);
    ss.str("");
	if(canUndo() && !endgame)
		ss << "U: Refazer jogada.";
	else
		ss << "Nao e possivel retroceder.";
	drawString(ss.str().c_str(), 1, 10, color, font);
	ss.str("");
	ss << players[turn]->getNome() << " tem a vez.";
	drawString(ss.str().c_str(), 1, 20, color, font);
	ss.str("");
	if(gamemsg.size() > 0)
	{
		ss << gamemsg;
		drawString(ss.str().c_str(), 235, 10, color, font);
		ss.str("");
	}
	if(endgame && !anim)
	{
		ss << players[winner]->getNome() << " vence!";
		drawString(ss.str().c_str(), 150, 150, color, font2);
		ss.str("");
	}
    ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield);
    glPopMatrix();              
    glMatrixMode(GL_MODELVIEW);  
    glPopMatrix();                 
}

void CenarioJogo::drawString(const char *str, int xt, int yt, float color[4], void *font)
{
    glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask
    glDisable(GL_LIGHTING);     // need to disable lighting for proper text color
    glColor4fv(color);          // set text color
    glRasterPos2i(xt, yt);        // place text position
    // loop all characters in the string
    while(*str)
    {
        glutBitmapCharacter(font, *str);
        ++str;
    }
    glEnable(GL_LIGHTING);
    glPopAttrib();
}

void CenarioJogo::toggleLight()
{
	if(light)
		light = false;
	else
		light = true;
}

void CenarioJogo::setSimpleMoveAnimation(int xi, int yi, int fx, int fy)
{
	char* j;	
	if(turn == 0)
		j = "b";
	else
		j = "r";
	endgame = com->game_end(j, (char*) convertCasa(xi,yi).c_str(), (char*) convertCasa(fx,fy).c_str());
	pecaanim = t->getCasa(xi,yi);
	prevx = x = xi;
	prevy = y = yi;
	xf = fx;
	yf = fy;
	timer = 100;
	dx = (xf - x) / timer;
	dy = (yf - y) / timer;
	t->clearCasa(xi,yi);
	anim = true;
	tumbling = false;
	Jogada* jog = new Jogada(turn, xi, yi, fx, fy, tumbling,pecaanim);
	jogadas.push_back(jog);
}

void CenarioJogo::setTumblingMoveAnimation(int xi, int yi, int dir)
{
	char* j;	
	if(turn == 0)
		j = "b";
	else
		j = "r";
	prevx = x = xi;
	prevy = y = yi;
	pecaanim = t->getCasa(xi,yi);
	tumblingsize = pecaanim.size();
	deslx = 0;
	desly = 0;
	if(dir == N || dir == NE || dir == NW)
		desly = - tumblingsize;
	else if(dir == S || dir == SW || dir == SE)
		desly = tumblingsize;
	if(dir == W || dir == SW || dir == NW)
		deslx = - tumblingsize;
	else if(dir == E || dir == NE || dir == SE)
		deslx = tumblingsize;
	xf = x + deslx;
	yf = y + desly;
	timer = 100;
	dx = (xf - x) / timer;
	dy = (yf - y) / timer;
	anim = true;
	tumbling = true;
	t->clearCasa(xi,yi);
	for(int lim = 1; lim <= tumblingsize; lim++)
	{
		int actx = xi+(lim*(deslx/tumblingsize));
		int acty = yi+(lim*(desly/tumblingsize));
		if(actx > 7)
			actx = 7;
		if(actx < 0)
			actx = 0;
		if(acty > 7)
			acty = 7;
		if(acty < 0)
			acty = 0;
		if(t->isKing(actx,acty) && !(t->isOwner(actx,acty,turn)) && (pecaanim[lim-1].getCor() == turn))
			endgame = true;		
	}
	Jogada* jog = new Jogada(turn, xi, yi, xf, yf, tumbling,pecaanim);
	jogadas.push_back(jog);
}

bool CenarioJogo::processMovement(int xi, int yi, int fx, int fy, bool tumb)
{
	char* tipomov;
	if(tumb)
		tipomov = "T";
	else
		tipomov = "M";
	char* jog;
	if(turn == 0)
		jog = "b";
	else
		jog = "r";
	if((com->execute((char*) convertGameStatus().c_str(), jog, xi, yi, fx, fy, tipomov)))
	{
		if(tumb)
		{
			if(((fx - xi) == 0) || ((fy - yi) == 0) || (abs(fx - xi) == abs(fy - yi)))
			{
				int d;
				if(xi == fx)
				{
					if(yi < fy)
						d = S;
					else
						d = N;
				}
				else
				{
					if(xi < fx)
					{
						if(yi < fy)
							d = SE;
						else if(fy < yi)
							d = NE;
						else
							d = E;
					}
					else
					{
						if(yi < fy)
							d = SW;
						else if(fy < yi)
							d = NW;
						else
							d = W;
					}
				}
				setTumblingMoveAnimation(xi, yi, d);
				return true;
			}
		}
		else
		{
			if(((abs(fx - xi) >= 0) && (abs(fx - xi) <= 1)) && ((abs(fy - yi) >= 0) && (abs(fy - yi) <= 1)))
			{
				setSimpleMoveAnimation(xi, yi, fx, fy);
				return true;
			}
		}
	}
	return false;
}

void CenarioJogo::setSimpleMoveUndo(Jogada* jog)
{
	t->setCasa(jog->getInitialX(),jog->getInitialY(),jog->getPecas());
	for(unsigned int iter = 0; iter < jog->getPecas().size(); iter++)
		t->decreaseCasa(jog->getFinalX(),jog->getFinalY());
}

void CenarioJogo::setTumblingMoveUndo(Jogada* jog)
{
	int deltax = 0;
	int deltay = 0;
	if((jog->getFinalX() - jog->getInitialX()) != 0)
	{
		if(jog->getFinalX() > jog->getInitialX())
			deltax = 1;
		else
			deltax = -1;
	}	
	if((jog->getFinalY() - jog->getInitialY()) != 0)
	{
		if(jog->getFinalY() > jog->getInitialY())
			deltay = 1;
		else
			deltay = -1;
	}
	int itex = jog->getInitialX();
	int itey = jog->getInitialY();
	int lim = 1;
	do{
		int pritex = jog->getInitialX()+(deltax*lim);
		int pritey = jog->getInitialY()+(deltay*lim);
		if(pritex < 0)
		{
			break;
		}
		if(pritex > 7)
		{
			break;
		}
		if(pritey < 0)
		{
			break;
		}
		if(pritey > 7)
		{
			break;
		}
		itex = pritex;
		itey = pritey;
		t->decreaseCasa(itex,itey);
		lim++;
	}while(lim <= jog->getPecas().size());
	while(lim <= jog->getPecas().size())
	{
		t->decreaseCasa(itex,itey);
		lim++;
	}
	t->setCasa(jog->getInitialX(),jog->getInitialY(),jog->getPecas());
}

void CenarioJogo::processUndo()
{
	if(canUndo() && !anim)
	{
		int pt;
		do{
			if(jogadas.back()->getTumbling())
				setTumblingMoveUndo(jogadas.back());
			else
				setSimpleMoveUndo(jogadas.back());
			pt = jogadas.back()->getPlayer();
			jogadas.pop_back();
		}while(pt != turn);
	}
}

void CenarioJogo::changeTexture()
{
	t->setNextTexture();
}

bool CenarioJogo::isCasaNotEmpty(int cx, int cy)
{
	vector<Peca> selectedcasa = t->getCasa(cx,cy);
	if(selectedcasa.empty())
		return false;
	else
		return true;
}

bool CenarioJogo::isCasaOwner(int cx, int cy)
{
	return t->isOwner(cx,cy,turn);
}

bool CenarioJogo::isAnimating()
{
	return anim;
}

string CenarioJogo::convertGameStatus()
{
	string statustab = "[\n";
	for(int cy = 0; cy < 8; cy++)
	{
		statustab = statustab + "[";
			for(int cx = 0; cx < 8; cx++)
			{
				statustab = statustab + convertCasa(cx,cy);
				if(cx < 7)
					statustab = statustab + ",";
			}
		statustab = statustab + "]";
		if(cy < 7)
			statustab = statustab + ",\n";
	}
	statustab = statustab + "]";
	return statustab;
}

string CenarioJogo::convertCasa(int cx, int cy)
{
	vector<Peca> pecascasa = t->getCasa(cx,cy);
	string casa = "";
	unsigned int corserie = 2;
	unsigned int coractual;
	int conta = 0;
	bool maisqueuma = false;
	if(pecascasa.size() > 0)
	{
		if(pecascasa.size() == 1)
		{
			if(pecascasa[0].getCor() == 0)
				casa = "[b-1]";
			else
				casa = "[r-1]";
		}
		else
		{		
		for(unsigned int pc = 0; pc < pecascasa.size(); pc++)
		{
			coractual = pecascasa[pc].getCor();
			if(coractual == corserie && pc < (pecascasa.size()-1))
			{
				conta++;
			}
			else
			{
				if(pc == (pecascasa.size()-1) && (coractual == corserie))
					conta++;
				if(conta > 0)
				{
					std::stringstream conjunto;
					if(corserie == 0)
						conjunto << "b-" << conta;
					else
						conjunto << "r-" << conta;
					casa = casa + "[" + conjunto.str() + "]";
					conjunto.clear();
					if(pc < (pecascasa.size()-1))
					{
						casa = casa + ",";
						maisqueuma = true;
					}
				}
				corserie = coractual;
				conta = 1;
			}
		}
		}
	}
	else
	{
		casa = "0";
	}
	if(maisqueuma)
		casa = "[" + casa + "]";
	return casa;
}

Jogador* CenarioJogo::getPlayer(int j)
{
	return players[turn];
}

int CenarioJogo::getTurn()
{
	return turn;
}

bool CenarioJogo::canUndo()
{
	return (jogadas.size() >= 2);
}

void CenarioJogo::select(int cx, int cy)
{
	t->selectCasa(cx,cy);
}

void CenarioJogo::deselect()
{
	t->deselectCasas();
}

void CenarioJogo::printPlayList()
{
	printf("Jogadas ate ao momento:\n");
	for(unsigned int pl = 0; pl < jogadas.size(); pl++)
	{
		printf("%d | %s = %d:%d -> %d:%d",pl,players[jogadas[pl]->getPlayer()]->getNome().c_str(),jogadas[pl]->getInitialX(),jogadas[pl]->getInitialY(),jogadas[pl]->getFinalX(),jogadas[pl]->getFinalY());
		if(jogadas[pl]->getTumbling())
			printf(" tumbling");
		printf("\n");
	}
}

bool CenarioJogo::isGameOver()
{
	return endgame;
}

void CenarioJogo::endTheGame()
{
	winner = turn;
	endtime = clock();
}

void CenarioJogo::restartGame()
{
	endgame = false;
	t = new Tabuleiro();
	jogadas.clear();
	turn = 0;
	pecaanim.clear();
	anim = false;
	tumbling = false;
	tumblingsize = 0;
	start = clock();
	winner = -1;
	timer = -1;
	t->deselectCasas();
}

void CenarioJogo::closeCom()
{
	com->quit();
}

void CenarioJogo::setMessage(string msg)
{
	gamemsg = msg;
}

void CenarioJogo::clearMessage()
{
	gamemsg.clear();
}